/*
0 * Listener.java
 * This is the agent. Agent has three handlers
 * HandleAgentStartProcess
 * 		get the relayed connect request from the controller, use AppProcess to start the server-side
 * 		application, and then send the application port and address to the controller
 * HandleAgentManageProcess
 * 		monitor the connection start message and destroy message from the server-side application
 * 		and relay the message to the controller
 * HandleAgentRegister
 * 		When controller initialize, it will register itself to every agent, so that agent know where
 * 		the controller is. This design allow the number of agents to expand as will with only one piece
 * 		of config file placed in the controller server
 * 
 * */

package wps.servicebus.agent;

import wps.servicebus.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.servlet.Context;

import wps.servicebus.ServHandler;

//handle the create process request sending from the controller
class HandleAgentStartProcess extends ServHandler
{	
/*	private String localAddr;
	private int localPort;
	public HandleAgentStartProcess(String addr, int port){
		this.localAddr = addr;
		this.localPort = port;
	}*/
	static Logger logger = Logger.getLogger(HandleAgentStartProcess.class);
	
	@Override
	public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
		throws IOException, ServletException
	{
		if(!validate(this.getServer(), request)) return;
		
		System.out.println("agent receive request from controller...");		
		String user = request.getParameter("user");
		String token = request.getParameter("token");
		String file = request.getParameter("file");
		String key = request.getParameter("key");
//		Logger.println("Port: " + request.getServerPort() + " user: " + user + ", token: " + token + ", file: " + file + ", key: " + key);
				
		int port = -1;		
		port = startProcess(request.getLocalPort());
				
		String res = "-addr " + request.getLocalAddr() + " -port " + port;
		
		//send the socket information back to the controller
//		System.out.println(res);
		OutputHtml(response, res);	
		((Request)request).setHandled(true);
	}
	private synchronized int startProcess(int monitorPort){
		AppProcess inst = new AppProcess();
		Random random = new Random();
		while(true){
			int port = 10000 + Math.abs(random.nextInt() % 100);
			ServerSocket socket;
			try {
				socket = new ServerSocket(port);
				socket.close();
				inst.Start(port, monitorPort);
				return port;
			} catch (IOException e) {
				logger.debug("port in use, creating another random port number...");
			}
		}
	}
}
/*
 * monitor succeed or failure message from application, and relay the
 * control message to the controller's monitor.
 * Signature:
 * command = start/destroy : successfully setup connection with the client/vice versa
 * port = $port : the application port
 * processID = $processID : the application processID
 * 
 * 
 */

class HandleAgentManageProcess extends ServHandler
{
	public HandleAgentManageProcess(){
	}
	
	@Override
	public void handle(String target, HttpServletRequest request,
			HttpServletResponse response, int dispatch) throws IOException,
			ServletException {
		System.out.println("agent receive monitor information from application...");
		if(!isControllerAvailable(this.getServer())){
			return;
		}		
		String command = request.getParameter("command");
		int port = Integer.parseInt(request.getParameter("port"));
		int processID = Integer.parseInt(request.getParameter("processID"));		
		String controllerAddr = (String)this.getServer().getAttribute("controllerAddr");
		int controllerPort = Integer.parseInt(this.getServer().getAttribute("controllerPort").toString());
		HttpClient httpclient = new DefaultHttpClient();
		HttpGet httpget = new HttpGet(Network.getHttpURL(controllerAddr, controllerPort) 
				+ "monitor/?command="+command+"&addr="+ request.getLocalAddr()+"&agentPort="+request.getLocalPort()+"&appPort="+port+"&processID="+processID);			
		httpclient.execute(httpget);	
		httpget.abort();
		
		((Request)request).setHandled(true);
	}	
}

/*
 * get initialization request and setup connection with the controller
 * all the configuration files are stored in the controller side, so
 * the agent doesn't have to know who is the controller until controller
 * actively contact the agent with some authentication mechanism.
 * signature:
 * addr = $addr
 * port = $port 
 * 
 * */
class HandleAgentRegister extends ServHandler{

	@Override
	public void handle(String target, HttpServletRequest request,
			HttpServletResponse response, int dispatch) throws IOException,
			ServletException {
		if(isControllerAvailable(this.getServer())){
			System.out.println("controller already set, cannot set twice...");
			return;
		}
		System.out.println("agent being initialized by controller...");
		String controllerAddr = request.getParameter("addr");
		int controllerPort = Integer.parseInt(request.getParameter("port"));
		this.getServer().setAttribute("controllerAddr", controllerAddr);
		this.getServer().setAttribute("controllerPort", Integer.toString(controllerPort));
		System.out.println("agent successfully initialized...");
		((Request)request).setHandled(true);
	}
}

/*class Monitor extends Thread{
	
	private ServerSocket m_serverSocket = null;
	private int port = -1;
	
	public int getPort(){
		return port;
	}
	
	Monitor() throws IOException{			
		Random random = new Random();
		while(true){				
			port = 10000 + random.nextInt() % 5000;
			if(Network.isPortAvailable(port)){
				m_serverSocket = new ServerSocket(port);
				System.out.println("port is setup on " + port);
				start();
				break;
			}				
		}		
	}
	
	@Override
	public void run(){
		while(true){			
			try {
				final Socket clientSocket = m_serverSocket.accept();
				new Thread(){
					public void run(){
						BufferedReader in = null;
						try {
							in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
							String inputLine;
							while((inputLine = in.readLine()) != null){
								System.out.println(inputLine);
								System.out.println(Network.Protocol.parseQuitMessage(inputLine));
							}
						} catch (IOException e) {
							// TODO Auto-generated catch block
							try {
								in.close();
							} catch (IOException e1) {
								// TODO Auto-generated catch block								
							}
						}						
					}					
				}.start();				
			} catch (IOException e) {
				// TODO Auto-generated catch block				
			}
		}
	}
	
}*/

public class AgentApp
{
	private Server m_server = null;	
//	private Monitor m_monitor = null;	

	private void MapUrl(ContextHandlerCollection contexts, String path, AbstractHandler handler)
	{
		Context ctx = new Context();
		ctx.setContextPath(path);
		ctx.setHandler(handler);
		contexts.addHandler(ctx);
	}
	
	private void MapUrls()
	{
		ContextHandlerCollection contexts = new ContextHandlerCollection();
	//	MapUrl(contexts, "/", new ServHandler.HandleTest());
		MapUrl(contexts, "/start", new HandleAgentStartProcess());
		MapUrl(contexts, "/manage", new HandleAgentManageProcess());
		MapUrl(contexts, "/register", new HandleAgentRegister());
		
		HandlerCollection handlers = new HandlerCollection();
		handlers.setHandlers(new Handler[]{contexts,new DefaultHandler()});
		m_server.setHandler(handlers);
	}
	
	public void Init(int port)
	{
		try
		{			
	//		m_monitor = new Monitor();			
			m_server = new Server(port);
			MapUrls();			
			m_server.start();
			System.out.println("Starting Agent on " + port + "...");
			
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	
	public static void main(String[] args)
	{
		String addr = "127.0.0.1";
		int port = 9000;
		AgentApp inst = new AgentApp();
		inst.Init(port);
	}
}
